using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using HQLAddin.Intellisense;
using HqlIntellisense.ConfigurationAbstraction;
using Iesi.Collections;
using NHibernate.Cfg;

namespace HQLAddin.ConfigurationInspector
{
	public class ConfigurationArtifacts
	{
		public IDictionary<string, string> Imports { get; set; }
		public IEnumerable<IPersistentClassInformation> ClassInformation { get; set; }
		public IEnumerable<ConfigurationError> Errors { get; set; }
	}

	[Serializable]
	public class ConfigurationError
	{
		public string Description { get; set; }
	}

	public interface IAppDomainProxy
	{
		ConfigurationArtifacts GetArtifacts(string path);
	}
	
	

	/// <summary>
	/// A gateway to call our configuration lookup in a different app domain and shutdown this.
	/// untested and dirty
	/// </summary>
	[Export(typeof(IAppDomainProxy))]
	internal class AppDomainProxy : IAppDomainProxy
	{
		private static readonly IEnumerable<Assembly> Assemblies = new List<Assembly>
			  	{
			  		typeof (Configuration).Assembly,
			  		typeof (ISet).Assembly,
			  		typeof (ConfigurationProvider).Assembly,
			  		typeof (ConfigurationMapper).Assembly
			  	};

		public virtual ConfigurationArtifacts GetArtifacts(string path)
		{
			//var pathResolver = new StartupProjectOutputPathResolver();
			//var paths = pathResolver.FindPaths().ToArray();
			//if (paths.Count() == 0) return;

			string directory = Directory.GetCurrentDirectory();

			// Construct and initialize settings for a second AppDomain.
			var ads = new AppDomainSetup
			{
				ApplicationBase = Path.GetDirectoryName(GetType().Assembly.Location),
				DisallowBindingRedirects = false,
				DisallowCodeDownload = true,
				ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile,
				AppDomainInitializerArguments = new[] { path },
				AppDomainInitializer = args => Directory.SetCurrentDirectory(args.First())
			};


			// Create the second AppDomain.

			AppDomain.CurrentDomain.AssemblyResolve += ResolveCurrentAssembly;
			AppDomain safeDomain = AppDomain.CreateDomain(Guid.NewGuid().ToString(), null, ads);
			safeDomain.AssemblyResolve += ResolveAssemblyInStartupFolder;

			var instanceAndUnwrap = safeDomain.CreateInstanceAndUnwrap(
				Assembly.GetExecutingAssembly().FullName,
				typeof(ConfigurationLookup).FullName, true,
				BindingFlags.Instance | BindingFlags.Public, null, new[] { path },
				CultureInfo.CurrentCulture, null
				);

			var lookup = (ConfigurationLookup)instanceAndUnwrap;
			var result = new ConfigurationArtifacts
			             	{
			             		ClassInformation = lookup.GetClasses(), 
								Imports = lookup.GetImports(),
								Errors = lookup.GetErrors()
			             	};

			AppDomain.Unload(safeDomain);
			AppDomain.CurrentDomain.AssemblyResolve -= ResolveCurrentAssembly;
			Directory.SetCurrentDirectory(directory);
			return result;
		}

		private static Assembly ResolveCurrentAssembly(object sender, ResolveEventArgs args)
		{
			Assembly assembly = Assemblies.FirstOrDefault(a => a.FullName == args.Name);
			return assembly;
		}

		private static Assembly ResolveAssemblyInStartupFolder(object sender, ResolveEventArgs args)
		{
			try
			{
				string[] parts = args.Name.Split(',');
				string file = string.Format(@"{0}\{1}.dll", Directory.GetCurrentDirectory(), parts[0].Trim());
				return !File.Exists(file) ? null : Assembly.LoadFrom(file);
			}
			catch
			{
				return null;
			}
		}
	}
}